﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NEmu;
using Super64.Core.CPU.MIPS;
using System.Reflection;

namespace Super64.Core.CPU.MIPS.Interpreter
{
    public sealed partial class MipsInterpreter : MipsEngine
    {
        Dictionary<MipsOpcode, InstructionHandler> m_OpcodeCallTable;

        public MipsInterpreter(N64Machine parentMachine)
            : base(parentMachine)
        {
            m_OpcodeCallTable = new Dictionary<MipsOpcode, InstructionHandler>();
            CacheInstructionHandlers();
        }

        public override void Step()
        {


            //MipsInstruction inst = new MipsInstruction(Tools.Create32(
            //    (byte)BootCode.ReadByte(),
            //    (byte)BootCode.ReadByte(), 
            //    (byte)BootCode.ReadByte(), 
            //    (byte)BootCode.ReadByte()));

            //// Get opcode
            //MipsOpcode op = Disassembler.GetOpcode(inst);

            //CallInstruction(op, inst);

            //Console.WriteLine(BootCode.Position.ToString("X8") + " " + inst.RawInstruction.ToString("X8") + " " + op.ToString());
        }

        #region Private

        private void CacheInstructionHandlers()
        {
            // Clear the hashtable
            m_OpcodeCallTable.Clear();

            // First get all the methods of this class instance
            MethodInfo[] methods = this.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic);

            // Loop through all the methods for InstructionHandlerType attributes
            foreach (MethodInfo method in methods)
            {
                object[] attributes = method.GetCustomAttributes(typeof(InstructionHandlerType), false);

                // If we find one, then create a delegate of the method
                if (attributes.Length > 0)
                {
                    // Get opcode
                    MipsOpcode opcode = ((InstructionHandlerType)attributes[0]).Opcode;

                    // Create the delegate
                    InstructionHandler handler = (InstructionHandler)Delegate.CreateDelegate(typeof(InstructionHandler), this, method, false);

                    // Add it to the table
                    m_OpcodeCallTable.Add(opcode, handler);
                }
            }
        }

        private void CallInstruction(MipsOpcode opcode, MipsInstruction inst)
        {
            InstructionHandler handler;

            if (m_OpcodeCallTable.TryGetValue(opcode, out handler))
            {
                handler(inst);
            }
        }

        #endregion

        #region Internals

        internal delegate void InstructionHandler(MipsInstruction inst);

        internal sealed class InstructionHandlerType : Attribute
        {
            MipsOpcode m_Opcode;

            public InstructionHandlerType(MipsOpcode opcode)
            {
                m_Opcode = opcode;
            }

            public MipsOpcode Opcode
            {
                get { return m_Opcode; }
            }
        }

        #endregion
    }
}
